package com.idanchuang.income;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

public class TableBuilder implements Serializable {

    private static String[] types = {"自媒体", "社群管理", "宣传物料", "私享会"};
    private static String[] brandA = {"Vida Glow", "melrose", "Lovekins", "Minenssey", "Dr.LeWinn's", "THALISSI", "Alissi Bronte", "Amilera"};
    private static String[] brandB = {"Robert Oatley", "eimele", "SENSORI+", "BURGE FAMILY", "NU-LAX", "Paulett Wines", "MitoQ®"};
    private static String[] brandC = {"Hunter Lab", "INVISIBLE ZINC", "Penfolds", "BEE+", "DOVERIST", "ch22"};
    private static String[] brandD = {"贵州习酒", "大摩", "Thorne", "五粮液"};

    private static Random r = new Random();

    private static float[] getArrayRandoms(int length, float base) {
        float[] ratios = new float[length];
        float sum = 0;
        for (int i = 0; i < ratios.length - 1; i++) {
            float f = (float) r.nextInt(length * 100) * (float) 1.2 / base;
            if (sum + f > 1) {
                f = 1 - sum;
            }
            ratios[i] = f;
            sum += f;
        }
        ratios[ratios.length - 1] = 1 - sum;
        return ratios;
    }

    /**
     * 获取候选品牌列表
     *
     * @param brands
     * @param total
     * @param rate
     * @return
     */
    private static List<String> getBrands(String[] brands, int total, float rate) {
        int size = (int) ((float) total * rate);
        Integer[] index = getRandomArrayIndex(size, brands.length);
        List<String> selected = new LinkedList<>();
        for (int i = 0; i < index.length; i++) {
            selected.add(brands[index[i]]);
        }
        return selected;
    }

    private static Integer[] getRandomArrayIndex(int size, int capital) {
        Set<Integer> set = new HashSet<>();
        while (set.size() < size) {
            set.add(r.nextInt(capital));
        }
        return set.stream().sorted().toArray(Integer[]::new);
    }

    public static RateTable[] readRateTable() {
        try {
            InputStream inputStream = TableBuilder.class.getClassLoader().getResourceAsStream("rate.obs");
            try (ObjectInputStream input = new ObjectInputStream(inputStream)) {
                return (RateTable[]) input.readObject();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    public static RateTable[] getRateTable() {
        try {
            File file = new File("rate.obs");
            RateTable[] rate = null;
            if (file.exists()) {
                try (FileInputStream fileInput = new FileInputStream(file)) {
                    try (ObjectInputStream input = new ObjectInputStream(fileInput)) {
                        rate = (RateTable[]) input.readObject();
                    }
                }
            } else {
                rate = buildRateTable();
                try (FileOutputStream fileOut = new FileOutputStream(file)) {
                    try (ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
                        out.writeObject(rate);
                    }
                }
            }
            return rate;
        } catch (Exception e) {
            return null;
        }
    }

    private static RateTable[] buildRateTable() {
        int suites = 100;
        RateTable[] rateTables = new RateTable[suites];
        for (int i = 0; i < suites; i++) {
            int brandsCount = r.nextInt(7) + 8;
            List<String> brands = new LinkedList<>();
            brands.addAll(getBrands(brandA, brandsCount, 0.5F));
            brands.addAll(getBrands(brandB, brandsCount, 0.2F));
            brands.addAll(getBrands(brandC, brandsCount, 0.15F));
            brands.addAll(getBrands(brandD, brandsCount, 0.15F));
            RateTable table = new RateTable(brands);
            float[] brandRate = getArrayRandoms(brands.size(), 10000);
            Arrays.sort(brandRate);
            brandRate = reserve(brandRate);
            for (int j = 0; j < brands.size(); j++) {
                String brand = brands.get(j);
                float[] rate = getArrayRandoms(types.length, 1200);
                for (int k = 0; k < rate.length; k++) {
                    rate[k] = rate[k] * brandRate[j];
                }
                table.addBrandRate(brand, rate);
            }
            rateTables[i] = table;
        }
        return rateTables;
    }


    public static float[] reserve(float[] arr) {
        float[] arr1 = new float[arr.length];
        for (int x = 0; x < arr.length; x++) {
            arr1[x] = arr[arr.length - x - 1];
        }
        return arr1;
    }


    private static void printUserMonthIncoming(RateTable[] rateTables, int userId, float[] incomings) {
        System.out.println("=======" + userId + "=======");
        for (int i = 0; i < incomings.length; i++) {
            if (userId % 100 == 0) {
                userId = userId + 1;
            }
            RateTable selected = rateTables[userId * (i + 8) % 100];
            selected.apply(incomings[i]);
            System.out.println(selected.printIncoming());
        }
    }

    public static void main(String[] args) throws Exception {
        RateTable[] rateTables = readRateTable();

//        printUserMonthIncoming(rateTables, 1053194, new float[]{0F, 0F, 0F});
//        printUserMonthIncoming(rateTables, 1593673, new float[]{24433.26F, 33172.25F, 14622.11F});
        printUserMonthIncoming(rateTables, 6334447, new float[]{176520.12F, 198036.33F, 144668.13F});
//        printUserMonthIncoming(rateTables, 458912, new float[]{37447.03F, 83302.13F, 30375.34F});
//        printUserMonthIncoming(rateTables, 1341895, new float[]{165458.69F, 157676.66F, 101158.47F});
    }


    public static class RateTable implements Serializable {

        private static final long serialVersionUID = 3594041868879603824L;

        private List<String> brands;
        private Map<String, float[]> rates;
        private transient Map<String, float[]> incomings;

        public Map<String, float[]> getIncomings() {
            return incomings;
        }

        public RateTable(List<String> brands) {
            this.brands = brands;
            this.rates = new HashMap<>(brands.size());
        }

        public void addBrandRate(String brand, float[] rate) {
            float sum = 0;
            for (int i = 0; i < rate.length; i++) {
                sum += rate[i];
            }
            if (sum > 0) {
                rates.put(brand, rate);
            } else {
                brands.remove(brand);
            }
        }

        public void apply(float incoming) {
            incomings = new HashMap<>();
            for (String key : rates.keySet()) {
                float[] rate = rates.get(key);
                float[] incomingDetail = new float[rate.length];
                for (int i = 0; i < rate.length; i++) {
                    BigDecimal bd = new BigDecimal((double) incoming * rate[i]);
                    bd = bd.setScale(2, 4);
                    incomingDetail[i] = bd.floatValue();
                }
                incomings.put(key, incomingDetail);
            }
        }

        public String printIncoming() {
            if (incomings != null) {
                return print(incomings);
            }
            return "";
        }

        private String print(Map<String, float[]> content) {
            StringJoiner joiner = new StringJoiner("\n");
            StringJoiner tabJoiner = new StringJoiner("\t");
            tabJoiner.add("品牌");
            for (int i = 0; i < types.length; i++) {
                tabJoiner.add(types[i]);
            }
            joiner.add("\t").add(tabJoiner.toString());
            brands.stream().forEach(b -> {
                StringJoiner tab = new StringJoiner("\t");
                float[] rate = content.get(b);
                if (rate != null) {
                    tab.add(b);
                    for (int i = 0; i < rate.length; i++) {
                        tab.add(String.valueOf(rate[i]));
                    }
                }
                joiner.add(tab.toString());
            });
            return joiner.toString();
        }


        @Override
        public String toString() {
            return print(this.rates);
        }
    }

}
